// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
// SPDX-License-Identifier: MIT

package metrics

import (
	"errors"
	"fmt"
	"time"

	"github.com/beevik/ntp"
	"github.com/prometheus/client_golang/prometheus"
)

type NTPConfig struct {
	NtpServer          string        `yaml:"ntp_server"`
	NtpProtocolVersion int           `yaml:"ntp_protocol_version"`
	NtpServerIsLocal   bool          `yaml:"ntp_server_is_local"`
	NtpIPTTL           int           `yaml:"ntp_ip_ttl"`
	NtpMaxDistance     time.Duration `yaml:"ntp_max_distance"`
	NtpOffsetTolerance time.Duration `yaml:"ntp_offset_tolerance"`
}

type NTPCollector struct {
	config  NTPConfig
	metrics map[string]*MetricDesc
}

func NewNTPCollector(cfg *NTPConfig) *NTPCollector {
	const subsystem = "ntp"

	return &NTPCollector{
		config: *cfg,
		metrics: map[string]*MetricDesc{
			"stratum": {
				Desc: prometheus.NewDesc(
					prometheus.BuildFQName(namespace, subsystem, "stratum"),
					"NTP stratum",
					nil,
					nil,
				),
				Type: prometheus.GaugeValue,
			},
			"leap": {
				Desc: prometheus.NewDesc(
					prometheus.BuildFQName(namespace, subsystem, "leap"),
					"NTP leap indicator",
					nil,
					nil,
				),
				Type: prometheus.GaugeValue,
			},
			"rtt": {
				Desc: prometheus.NewDesc(
					prometheus.BuildFQName(namespace, subsystem, "rtt_seconds"),
					"NTP round-trip time",
					nil,
					nil,
				),
				Type: prometheus.GaugeValue,
			},
			"offset": {
				Desc: prometheus.NewDesc(
					prometheus.BuildFQName(namespace, subsystem, "offset_seconds"),
					"ClockOffset between NTP and local clock.",
					nil,
					nil,
				),
				Type: prometheus.GaugeValue,
			},
			"reftime": {
				Desc: prometheus.NewDesc(
					prometheus.BuildFQName(namespace, subsystem, "reference_time_seconds"),
					"Reference time from NTP server.",
					nil,
					nil,
				),
				Type: prometheus.GaugeValue,
			},
			"rootdelay": {
				Desc: prometheus.NewDesc(
					prometheus.BuildFQName(namespace, subsystem, "root_delay_seconds"),
					"NTP root delay",
					nil,
					nil,
				),
				Type: prometheus.GaugeValue,
			},
			"rootdispersion": {
				Desc: prometheus.NewDesc(
					prometheus.BuildFQName(namespace, subsystem, "root_dispersion_seconds"),
					"NTP root dispersion",
					nil,
					nil,
				),
				Type: prometheus.GaugeValue,
			},
		},
	}
}

func (c *NTPCollector) Update(ch chan<- prometheus.Metric) error {
	if c.config.NtpServer == "" {
		return errors.New("NTP server not configured")
	}

	// 查询NTP服务器
	response, err := ntp.Query(c.config.NtpServer)
	if err != nil {
		return fmt.Errorf("NTP query failed: %w", err)
	}

	// 发送指标
	ch <- prometheus.MustNewConstMetric(
		c.metrics["stratum"].Desc,
		c.metrics["stratum"].Type,
		float64(response.Stratum),
	)

	ch <- prometheus.MustNewConstMetric(
		c.metrics["leap"].Desc,
		c.metrics["leap"].Type,
		float64(response.Leap),
	)

	ch <- prometheus.MustNewConstMetric(
		c.metrics["rtt"].Desc,
		c.metrics["rtt"].Type,
		response.RTT.Seconds(),
	)

	ch <- prometheus.MustNewConstMetric(
		c.metrics["offset"].Desc,
		c.metrics["offset"].Type,
		response.ClockOffset.Seconds(),
	)

	ch <- prometheus.MustNewConstMetric(
		c.metrics["reftime"].Desc,
		c.metrics["reftime"].Type,
		float64(response.ReferenceTime.Unix()),
	)

	ch <- prometheus.MustNewConstMetric(
		c.metrics["rootdelay"].Desc,
		c.metrics["rootdelay"].Type,
		response.RootDelay.Seconds(),
	)

	ch <- prometheus.MustNewConstMetric(
		c.metrics["rootdispersion"].Desc,
		c.metrics["rootdispersion"].Type,
		response.RootDispersion.Seconds(),
	)

	return nil
}
